//
// Created by bill3000 on 2/14/21.
// Every thing need to do is to
// call start_timer_daemon() to start timer daemon
//

#include <csignal>
#include <sys/time.h>

#include "../fsm/macros.hpp"
#include "timer.hpp"
#include "../log/logger.hpp"

extern bool running;

TimerNode *OkTimer::timers = nullptr;
sem_t OkTimer::sem;
Queue *OkTimer::mq;

void OkTimer::start_timer_daemon() {
    OkTimer::timer_daemon();
}

void OkTimer::wait_timer_daemon() {
}

void OkTimer::stop_timer_daemon() {
}

void OkTimer::dispose() {
    signal(SIGALRM, SIG_DFL);

    TimerNode *tn = timers;
    while(tn != nullptr) {
        TimerNode *t = tn->next;
        delete tn;
        tn = t;
    }
    timers = nullptr;
    sem_destroy(&sem);
}

void OkTimer::timer_daemon() {
    timers = nullptr;
    int res = sem_init(&sem, 0, 1);
    if (res != 0) {
        LOG(ERROR, "Semaphore initialization failed in OkTimer !");
        exit(1);
    }
    mq = Queue::get_instance();

    signal(SIGALRM, check_timer);
    struct itimerval new_value = {0}, old_value = {0};
    new_value.it_value.tv_sec = 0;
    new_value.it_value.tv_usec = TIMER_INTERVAL * 1000;
    new_value.it_interval.tv_sec = 0;
    new_value.it_interval.tv_usec = TIMER_INTERVAL * 1000;  //every 10ms
    setitimer(ITIMER_REAL, &new_value, &old_value);
    LOG(INFO, "Start timer daemon successfully !");
}

void OkTimer::add_timer(TimerNode &tn) {
    auto *t = new TimerNode();
    *t = tn;
    sem_wait(&sem);
    t->next = timers;
    timers = t;
    sem_post(&sem);
}

void OkTimer::check_timer(int) {
    sem_wait(&sem);
    TimerNode *tn = timers;
    TimerNode *prev = nullptr;
    while(tn != nullptr) {
        tn->howlong -= TIMER_INTERVAL;
        if (tn->howlong <= 0) {
            handle_time_out(*tn);
            if (prev == nullptr) {
                timers = tn->next;
                delete tn;
                tn = timers;
            }
            else {
                TimerNode *t = tn->next;
                delete tn;
                tn = t;
            }
        }
        else {
            prev = tn;
            tn = tn->next;
        }
    }
    sem_post(&sem);
}

void OkTimer::handle_time_out(TimerNode &t) {
    Message message;

    if (t.to == TIMER_TO_MECH) {
        message.mtype = MESSAGE_DIRECTION_SORTING_THREAD_TO_MECH;
        message.data.ctype = t.dowhat;
        message.data.param = t.param;
        mq->send_message(M03, &message);
    }
    else if (t.to == TIMER_TO_SORTING_KERNEL){
        message.mtype = MESSAGE_DIRECTION_UI_THREAD_TO_SORTING_THREAD;
        message.data.ctype = t.dowhat;
        message.data.param = t.param;
        mq->send_message(M02, &message);
    }
    else {
        LOG(WARNING, "How can I get this type of message here !");
    }
}

